'    Copyright 2008 Jan Molnar <jan.molnar@abds.de>
'
'    This file is part of JCQ.

'    JCQ is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 2 of the License, or
'    (at your option) any later version.

'    JCQ is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.

'    You should have received a copy of the GNU General Public License
'    along with JCQ. If not, see <http://www.gnu.org/licenses/>.

Public Class ServiceProvider(Of S As Interfaces.IService)
    Inherits Service
    Implements Interfaces.IServiceProvider(Of S)

    Private mServices As System.Collections.Generic.Dictionary(Of System.Type, S)
    Private _lock As System.Threading.ReaderWriterLock
    Private Const WAIT_TIMEOUT As Integer = 20000

    Public Sub New()
        _lock = New System.Threading.ReaderWriterLock

        mServices = New System.Collections.Generic.Dictionary(Of System.Type, S)
    End Sub

    Public Function GetService(Of C As S)() As C Implements Interfaces.IServiceProvider(Of S).GetService
        Dim serviceContract As System.Type
        Dim serviceImplementation As C
        Dim serviceImplementationType As System.Type
        Dim cookie As System.Threading.LockCookie

        serviceContract = GetType(C)

        Try
            _lock.AcquireReaderLock(WAIT_TIMEOUT)

            If Not mServices.ContainsKey(serviceContract) Then
                serviceImplementationType = Core.Kernel.Mapper.GetImplementationType(Of C)()

                For Each pair As KeyValuePair(Of Type, S) In mServices
                    If pair.GetType Is serviceImplementationType OrElse pair.GetType.IsSubclassOf(serviceImplementationType) OrElse serviceImplementationType.IsSubclassOf(pair.GetType) Then
                        serviceImplementation = DirectCast(pair.Value, C)
                    End If
                Next

                If serviceImplementation Is Nothing Then serviceImplementation = Core.Kernel.Mapper.CreateImplementation(Of C)()

                If Not serviceImplementation Is Nothing Then
                    cookie = _lock.UpgradeToWriterLock(WAIT_TIMEOUT)

                    Try
                        mServices.Add(serviceContract, serviceImplementation)
                    Finally
                        _lock.DowngradeFromWriterLock(cookie)
                    End Try
                End If
            Else
                serviceImplementation = DirectCast(mServices(serviceContract), C)
            End If
        Catch ex As Exception
            If TypeOf ex Is Interfaces.ServiceNotFoundException Then
                Throw
            Else
                Throw New Interfaces.ServiceNotFoundException(serviceContract, ex)
            End If
        Finally
            _lock.ReleaseReaderLock()
        End Try

        Return serviceImplementation
    End Function
End Class